/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.ListIterator;

/**
 *
 * @author endri
 */
public class VideoClient extends Thread {

    static String address;
    static int port;
    private String name;

    public VideoClient() throws UnknownHostException {
        name = java.net.InetAddress.getLocalHost().getHostName();

    }

    /**
     * @param none
     * @description Prints out to terminal the possible command line arguments.
     */
    public static void printHelp() {
        System.out.println("Possible command line arguments:\n"
                + "port=[port-number]\n"
                + "url=[url]\n"
                + "Example: java VideoClient port=9000 url=http://localhost\n"
                + "If no arguments given, default is used: http://localhost:9000");
    }

    public void run() {
        //System.out.println("Starting thread with ID: " + Thread.currentThread().getId());
        name += "_" + Thread.currentThread().getId();
        VideoDownloader vd = new VideoDownloader(address, port);

        try {
            //vd.downloadTimeRandomDownload(name);
            vd.smartWaitingDownload(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        // TODO code application logic here
        int length = args.length;
        int port = 9000;
        int numClients = 50;
        String address = "http://localhost";
        if (length > 3) {
            printHelp();
            System.exit(1);
        }

        while (length > 0) {
            StringTokenizer tokenizer = new StringTokenizer(args[length - 1], "=");
            String command = tokenizer.nextToken();
            if (command.contains("port") == true) {
                port = Integer.parseInt(tokenizer.nextToken());
                //System.out.println("found port: " + port);
            }
            if (command.contains("url") == true) {
                address = tokenizer.nextToken();
                if (address.contains("http://") == false) {
                    throw new IllegalArgumentException("URL invalid: does not contain 'http://'");
                }
                //System.out.println("found address: " + address);
            }
            if (command.contains("numClients") == true) {
                numClients = Integer.parseInt(tokenizer.nextToken());
                //System.out.println("numClients set to " + numClients);
            }
            length--;
        }
        VideoClient.address = address;
        VideoClient.port = port;
        //	VideoDownloader vd = new VideoDownloader(address, port);
        //vd.downloadTimeRandomDownload();
        /**
         * Just making the port and url available to all methods.
         */
        //	vd.smartWaitingWithRandomDownload();
        //getFileNames("http://localhost:9000");
        //normalDownloadStratedy();
        //smartWaitingDownload();
        //        smartWaitingWithRandomDownload();
        for (int i = 0; i < numClients; i++) {
            (new VideoClient()).start();
        }
        /*	(new VideoClient()).start();
         (new VideoClient()).start();*/
    }
}

class VideoDownloader {

    //example: http://localhost
    final String address;
    //example: 9000
    final int port;
    //example: http://localhost:9000
    final String url;
    //Length of video in s.
    final int videoLength = 10;
    //bandwidth in KB/s
    int bandwidth = 1270;

    VideoDownloader(String address, int port) {
        this.address = address;
        this.port = port;
        this.url = address + ":" + port;
    }


    public String pageDownload(String url) throws IOException {
        URLConnection connection = new URL(url).openConnection();
        String entirePage = "";
        Scanner urlReader = new Scanner(connection.getInputStream());
        while (urlReader.hasNextLine()) {
            String line = urlReader.nextLine();
            entirePage += line + "\n";
        }
        return entirePage;
    }
    /**
     * Downloads a website at the given url.
     *
     * This simply uses the well knows Scanner class, but we use a URLConnection
     * as input to Scanner instead of a file or System.in.
     *
     */
    public long download(String url) throws IOException {
        URLConnection connection = new URL(url).openConnection();
        long length = connection.getContentLengthLong();
        BufferedReader urlReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        while (urlReader.readLine()!=null) {            
        }
        return length;
    }
    
    private int downloadBandwidth(long length){
        return (int)(length/1000)/10 + 50;
    }

    public long download2(String url) throws IOException,InterruptedException {
        URLConnection connection = new URL(url).openConnection();
        String entirePage = "";
        DataInputStream urlReader = new DataInputStream(connection.getInputStream());
        int bytesGot = 0;
        int totalBytes = 0;
        long length = connection.getContentLengthLong();
        //        System.out.println("length: " + length + " bandwidth: " + bandwidth + " " + downloadBandwidth(length));
        int sleepTime = 1000 * 120 /(downloadBandwidth(length));
        while (totalBytes < length) {

            bytesGot=0;
            long timestampBefore = System.currentTimeMillis();
            while (totalBytes < length && bytesGot <= 120 * 1024) {
                urlReader.readByte();
                bytesGot += 1;
                totalBytes += 1;
            }
            long timestampAfter = System.currentTimeMillis();
            long difference = timestampAfter-timestampBefore;
            //            System.out.println("totalBytes: " + totalBytes + "\tLength: " + length);
            if(difference < sleepTime && (length - totalBytes > 0)){
                Thread.sleep(sleepTime-difference);
            }
        }
        return totalBytes;
    }

    /**
     * Get a LinkedList of strings containing all urls on the website at the
     * given url.
     *
     * You will probably have some problems understanding all this code unless
     * you are familiar with regular expressions. Do not worry about
     * understanding it, and just see how it is used.
     *
     */
    public LinkedList<String> getFileNames(String url) throws IOException {
        String entirePage = pageDownload(url);
        LinkedList<String> urlList = new LinkedList<String>();
        Pattern p = Pattern.compile("([0-9]+)\\.(supervid)");
        Matcher m = p.matcher(entirePage);
        while (m.find() == true) {
            urlList.add(m.group());
            //System.out.println(m.group());
        }
        return urlList;
        //System.out.println(entirePage);
    }

    public void normalDownloadStrategy(String name) throws IOException, InterruptedException {
        String currentSegment = "";

        while (true) {
            LinkedList<String> fileNames = getFileNames(url);
            Collections.sort(fileNames);

            if (!currentSegment.equals(fileNames.getLast())) {
                //we don't have the last file
                currentSegment = fileNames.getLast();
                long downloadTimestampBefore = System.currentTimeMillis();
                long timestampBefore = System.nanoTime()/1000000;
                long content = download(url + "/" + currentSegment);
                long timestampAfter = System.nanoTime()/1000000;
                long downloadTime = timestampAfter - timestampBefore;
                System.out.println(name + ";" + downloadTimestampBefore + ";" + timestampBefore +";" + timestampAfter + ";" + currentSegment + ";" + content + ";");
            }
            Thread.sleep(500);
        }
    }

    /*
     * Another strategy for download:
     * We can get the timestamp of when the last part was created and decide to wait
     * till the next part to poll (10 seconds from the first). 
     */
    public void smartWaitingDownload(String name) throws IOException, InterruptedException {
        String currentSegment = "";

        while (true) {
            long timestamp = System.currentTimeMillis();
            LinkedList<String> fileNames = getFileNames(url);
            Collections.sort(fileNames);

            if (!currentSegment.equals(fileNames.getLast())) {
                //we don't have the last file
                currentSegment = fileNames.getLast();
                StringTokenizer tokenizer = new StringTokenizer(currentSegment, ".");
                timestamp = Long.parseLong(tokenizer.nextToken());
                
                long downloadTimestampBefore = System.currentTimeMillis();
                long timestampBefore = System.nanoTime()/1000000;
                long content = download2(url + "/" + currentSegment);
                long timestampAfter = System.nanoTime()/1000000;
                long downloadTime = timestampAfter - timestampBefore;
                //                System.out.println("Downloadtime: " + downloadTime);
                System.out.println(name + ";" + downloadTimestampBefore + ";" + timestampBefore +";" + timestampAfter + ";" + currentSegment + ";" + content + ";");

                //System.out.println(currentSegment + "  " + content.length());
                long difference = (timestamp + 10) - (System.currentTimeMillis() / 1000);
                //System.out.println("Current Secs = " +
                //(System.currentTimeMillis() / 1000));
                
                if (difference > 0) {
                    //System.out.println("difference = " + difference);
                    Thread.sleep(difference * 1000);
                } else {
                    Thread.sleep(500); //just in case.. :P
                }
            } else {
                //System.out.println("Empty Poll!");
            }
        }

    }

    /*
     * Another downloading strategy could  be same as above, but make random waiting longer than the time the 
     * part is supposed to be produced. Of course playback has to start with a delay of 10 sec (second part) becase
     * in the first part we are going to buffer in order not to have a empty buffer ever.
     */

    public void smartWaitingWithRandomDownload(String name) throws IOException, InterruptedException {
        String currentSegment = "";
        Random rand = new Random();
        int segments;
        while (true) {
            long timestamp = System.currentTimeMillis()/1000;
            LinkedList<String> fileNames = getFileNames(url);
            Collections.sort(fileNames);

            if (!currentSegment.equals(fileNames.getLast())) {
                //we don't have the last file
                currentSegment = fileNames.getLast();
                StringTokenizer tokenizer = new StringTokenizer(currentSegment, ".");
                timestamp = Long.parseLong(tokenizer.nextToken());
                //System.out.println("timestamp = " + timestamp);

                //Download video
                long downloadTimestampBefore = System.currentTimeMillis();
                long timestampBefore = System.nanoTime()/1000000;
                long content = download(url + "/" + currentSegment);
                long timestampAfter = System.nanoTime()/1000000;
                long downloadTime = timestampAfter - timestampBefore;
                //                System.out.println("Downloadtime: " + downloadTime);
                System.out.println(name + ";" + downloadTimestampBefore + ";" + timestampBefore +";" + timestampAfter + ";" + currentSegment + ";" + content + ";");

                long difference = (timestamp + 10) - (System.currentTimeMillis() / 1000);
                //System.out.println("Current Secs = " +
                //(System.currentTimeMillis() / 1000));
                long random = 0;
                try {
                    random = difference*1000 + rand.nextInt((int) (10000-downloadTime));
                } catch (Exception e) {
                    System.out.println("HICKUP;"+ name + ";" + currentSegment + ";");
                }
                if (difference > 0) {
                    //                                        System.out.println("sleeping for: " + random);
                    Thread.sleep(random);
                } else {

                }
            } else {
                //System.out.println("Empty Poll!");
            }
        }
    }

    public void smartWaitingWithRandomDownload2(String name) throws IOException, InterruptedException {
        String currentSegment = "";
        Random rand = new Random();
        int segments;
        while (true) {
            long timestamp = System.currentTimeMillis();
            LinkedList<String> fileNames = getFileNames(url);
            Collections.sort(fileNames);

            if (!currentSegment.equals(fileNames.getLast())) {
                //we don't have the last file
                currentSegment = fileNames.getLast();
                StringTokenizer tokenizer = new StringTokenizer(currentSegment, ".");
                timestamp = Long.parseLong(tokenizer.nextToken());
                //System.out.println("timestamp = " + timestamp);

                //Download video
                long downloadTimestampBefore = System.currentTimeMillis();
                long timestampBefore = System.nanoTime()/1000000;
                long content = download(url + "/" + currentSegment);
                long timestampAfter = System.nanoTime()/1000000;
                long downloadTime = timestampAfter - timestampBefore;
                //                System.out.println("Downloadtime: " + downloadTime);
                System.out.println(name + ";" + downloadTimestampBefore + ";" + timestampBefore +";" + timestampAfter + ";" + currentSegment + ";" + content + ";");

                long difference = (timestamp + 10) - (System.currentTimeMillis() / 1000);
                //System.out.println("Current Secs = " +
                //(System.currentTimeMillis() / 1000));
                long random = 0;
                try {
                    random = difference*1000 + rand.nextInt((int) (10000-downloadTime));
                } catch (Exception e) {
                    System.out.println("HICKUP;"+ name + ";" + currentSegment + ";");
                }
                if (difference > 0) {
                    //                    System.out.println("sleeping for: " + random);
                    Thread.sleep(random);
                } else {

                }
            } else {
                //System.out.println("Empty Poll!");
            }
        }
    }

    /**
     * NOTE TO SELF: STOP THINKING OF ON-DEMAND STREAMING, DAMN IT!
     */
    public void downloadTimeRandomDownload(String name) throws IOException, InterruptedException {
        String currentSegment = "";
        Random rand = new Random();
        boolean firstSegmentReceived = false;
        long downloadTime = 0;//rand.nextInt(videoLength / 2);
        //for debugging	
        int segments = 0;
        long timestamp = 0;
        LinkedList<String> fileNames = null;
        while (true) {

            //Poll server
            fileNames = getFileNames(url);
            //No video segments on server yet.
            if (fileNames.isEmpty()) {
                //Poll the server again after an arbitrary length: videoLength/2
                System.out.println("No content.. sleeping");
                Thread.sleep(1000 * rand.nextInt(videoLength));
                continue;
            }

            Collections.sort(fileNames);
            //Polled and found segment. Let's sleep before downloading.
            System.out.println("Download time: " + downloadTime + " second(s)");
            if (downloadTime > 0 && downloadTime < 10) {
                Thread.sleep(rand.nextInt(10 - (int) downloadTime));
            }

            if (!currentSegment.equals(fileNames.getLast())) {
                System.out.println("Thread ID: " + Thread.currentThread().getId());
                currentSegment = fileNames.getLast();

                StringTokenizer tokenizer = new StringTokenizer(currentSegment, ".");
                timestamp = Long.parseLong(tokenizer.nextToken());
                long difference = (timestamp + 10) - (System.currentTimeMillis() / 1000);

                if (difference < downloadTime) {
                    System.out.println("WILL NOT MAKE IT: " + difference + " - " + downloadTime);
                    Thread.sleep(difference * 1000);
                    continue;
                }
                
                long before = System.currentTimeMillis() / 1000;
                System.out.println("Downloading: " + currentSegment);
                //Download video

                long content = download(url + "/" + currentSegment);

                long after = System.currentTimeMillis() / 1000;
                //Play video with new thread?
                //[..]
                downloadTime = after - before;
                System.out.println(name + ";" + before + ";" + after + ";" + currentSegment + ";" + content + ";");
                System.out.println("Time until next segment: " + (videoLength - downloadTime) + " second(s)");
                System.out.println("DownloadTime: " + downloadTime);
                if (downloadTime > 10) {
                    System.out.println("HICKUP;"+ name + ";" + currentSegment + ";");
                    continue;
                }
                Thread.sleep(1000 * (videoLength - downloadTime));
            } //If a new segment hasn't arrived yet.
            else {
                System.out.println("New segment has not arrived");
                //Poll the server again after a random amount of time
                Thread.sleep(500);
            }
        }
    } //end downloadTimeRandomDownload()

    public void pollRandomDownloadTimeRandomDownload() throws IOException, InterruptedException {
        String currentSegment = "";
        Random rand = new Random();
        boolean firstSegmentReceived = false;
        long downloadTime = rand.nextInt(videoLength / 2);
        //for debugging	
        int segments = 0;
        while (true) {
            LinkedList<String> fileNames = getFileNames(url);
            //No video segments on server yet.
            if (fileNames.isEmpty()) {
                //Poll the server again after an arbitrary length: videoLength/2
                //System.out.println("No content.. sleeping");
                Thread.sleep(1000 * rand.nextInt(videoLength));
                continue;
            }

            Collections.sort(fileNames);
            //Polled and found segment. Let's sleep before downloading.
            //System.out.println("Download time: " + downloadTime + " second(s)");
            if (downloadTime > 0) {
                Thread.sleep(rand.nextInt((int) downloadTime));
            }

            if (!currentSegment.equals(fileNames.getLast())) {
                //System.out.println("--------S" + segments++ + "--------");
                currentSegment = fileNames.getLast();
                long before = System.currentTimeMillis() / 1000;
                //System.out.println("Downloading: " + currentSegment);
                //Download video
                long content = download(url + "/" + currentSegment);
                //Simulating random DL time - debug
                Thread.sleep(1000 * rand.nextInt(videoLength / 2));
                long after = System.currentTimeMillis() / 1000;
                //Play video with new thread?
                //[..]
                downloadTime = after - before;
                //System.out.println("Time until next segment: " + (videoLength - downloadTime) + " second(s)");
                Thread.sleep(1000 * (videoLength - downloadTime));
            } //If a new segment hasn't arrived yet.
            else {
                //System.out.println("New segment has not arrived");
                //Poll the server again after a random amount of time
                Thread.sleep(1000 * rand.nextInt(videoLength));
            }
        }
    } //end pollRandomDownloadTimeRandomDownload()

    public void adaptiveDownload(String name) throws IOException, InterruptedException {
    }
}
